
#pragma once

#include <algorithm>
#include "../OgreMath/OgreVector3.h"
#include "../OgreMath/OgreMatrix3.h"
#include "../Prerequisites.h"

class SceneRenderer;

template<class T>
class PrecisionVector3
{
public:
	T x, y, z;

	inline PrecisionVector3()
	{
	}

	inline PrecisionVector3( const T tX, const T tY, const T tZ )
		: x( tX ), y( tY ), z( tZ )
	{
	}

	inline PrecisionVector3<T> crossProduct( const PrecisionVector3<T>& rkVector ) const
	{
		return PrecisionVector3<T>(
			y * rkVector.z - z * rkVector.y,
			z * rkVector.x - x * rkVector.z,
			x * rkVector.y - y * rkVector.x);
	}

	// arithmetic operations
	inline PrecisionVector3<T> operator + ( const PrecisionVector3<T>& rkVector ) const
	{
		return PrecisionVector3<T>(
			x + rkVector.x,
			y + rkVector.y,
			z + rkVector.z);
	}

	inline PrecisionVector3<T> operator - ( const PrecisionVector3<T>& rkVector ) const
	{
		return PrecisionVector3<T>(
			x - rkVector.x,
			y - rkVector.y,
			z - rkVector.z);
	}

	inline PrecisionVector3<T> operator * ( const T tScalar ) const
	{
		return PrecisionVector3<T>(
			x * tScalar,
			y * tScalar,
			z * tScalar);
	}

	inline PrecisionVector3<T> operator * ( const PrecisionVector3<T>& rhs) const
	{
		return PrecisionVector3<T>(
			x * rhs.x,
			y * rhs.y,
			z * rhs.z);
	}

	inline PrecisionVector3<T> operator / ( const T tScalar ) const
	{
		assert( tScalar != 0.0 );

		T tInv = 1.0 / tScalar;

		return PrecisionVector3<T>(
			x * tInv,
			y * tInv,
			z * tInv);
	}

	inline PrecisionVector3<T> operator / ( const PrecisionVector3<T>& rhs) const
	{
		return PrecisionVector3<T>(
			x / rhs.x,
			y / rhs.y,
			z / rhs.z);
	}

	inline const PrecisionVector3<T>& operator + () const
	{
		return *this;
	}

	inline PrecisionVector3<T> operator - () const
	{
		return PrecisionVector3<T>(-x, -y, -z);
	}

	// overloaded operators to help PrecisionVector3<T>
	inline friend PrecisionVector3<T> operator * ( const T tScalar, const PrecisionVector3<T>& rkVector )
	{
		return PrecisionVector3<T>(
			tScalar * rkVector.x,
			tScalar * rkVector.y,
			tScalar * rkVector.z);
	}

	inline friend PrecisionVector3<T> operator / ( const T tScalar, const PrecisionVector3<T>& rkVector )
	{
		return PrecisionVector3<T>(
			tScalar / rkVector.x,
			tScalar / rkVector.y,
			tScalar / rkVector.z);
	}

	inline friend PrecisionVector3<T> operator + (const PrecisionVector3<T>& lhs, const T rhs)
	{
		return PrecisionVector3<T>(
			lhs.x + rhs,
			lhs.y + rhs,
			lhs.z + rhs);
	}

	inline friend PrecisionVector3<T> operator + (const T lhs, const PrecisionVector3<T>& rhs)
	{
		return PrecisionVector3<T>(
			lhs + rhs.x,
			lhs + rhs.y,
			lhs + rhs.z);
	}

	inline friend PrecisionVector3<T> operator - (const PrecisionVector3<T>& lhs, const T rhs)
	{
		return PrecisionVector3<T>(
			lhs.x - rhs,
			lhs.y - rhs,
			lhs.z - rhs);
	}

	inline friend PrecisionVector3<T> operator - (const T lhs, const PrecisionVector3<T>& rhs)
	{
		return PrecisionVector3<T>(
			lhs - rhs.x,
			lhs - rhs.y,
			lhs - rhs.z);
	}

	// arithmetic updates
	inline PrecisionVector3<T>& operator += ( const PrecisionVector3<T>& rkVector )
	{
		x += rkVector.x;
		y += rkVector.y;
		z += rkVector.z;

		return *this;
	}

	inline PrecisionVector3<T>& operator += ( const T tScalar )
	{
		x += tScalar;
		y += tScalar;
		z += tScalar;
		return *this;
	}

	inline PrecisionVector3<T>& operator -= ( const PrecisionVector3<T>& rkVector )
	{
		x -= rkVector.x;
		y -= rkVector.y;
		z -= rkVector.z;

		return *this;
	}

	inline PrecisionVector3<T>& operator -= ( const T tScalar )
	{
		x -= tScalar;
		y -= tScalar;
		z -= tScalar;
		return *this;
	}

	inline PrecisionVector3<T>& operator *= ( const T tScalar )
	{
		x *= tScalar;
		y *= tScalar;
		z *= tScalar;
		return *this;
	}

	inline PrecisionVector3<T>& operator *= ( const PrecisionVector3<T>& rkVector )
	{
		x *= rkVector.x;
		y *= rkVector.y;
		z *= rkVector.z;

		return *this;
	}

	inline PrecisionVector3<T>& operator /= ( const T tScalar )
	{
		assert( tScalar != 0.0 );

		T tInv = 1.0 / tScalar;

		x *= tInv;
		y *= tInv;
		z *= tInv;

		return *this;
	}

	inline PrecisionVector3<T>& operator /= ( const PrecisionVector3<T>& rkVector )
	{
		x /= rkVector.x;
		y /= rkVector.y;
		z /= rkVector.z;

		return *this;
	}

	inline T normalise()
	{
		T tLength = std::sqrt( x * x + y * y + z * z );

		// Will also work for zero-sized vectors, but will change nothing
		// We're not using epsilons because we don't need to.
		// Read http://www.ogre3d.org/forums/viewtopic.php?f=4&t=61259
		if ( tLength > 0 )
		{
			T tInvLength = 1.0 / tLength;
			x *= tInvLength;
			y *= tInvLength;
			z *= tInvLength;
		}

		return tLength;
	}

	inline T dotProduct(const PrecisionVector3<T>& vec) const
	{
		return x * vec.x + y * vec.y + z * vec.z;
	}

	inline T operator [] ( const size_t i ) const
	{
		assert( i < 3 );

		return *(&x+i);
	}
};


struct RayTriangleIntersectionData
{
	PrecisionVector3<double> normal;

	// cached data for ray-triangle intersection test
	double aInv, bInv, cInv, dInv;
	unsigned short equationIndex1, equationIndex2;
};

class Ray
{
public:
	Ray(const Ogre::Vector3 &_origin, const Ogre::Vector3 &_direction) : origin(_origin), direction(_direction)
	{
		directionInv = Ogre::Vector3(1.0f, 1.0f, 1.0f) / direction;
		sign[0] = (directionInv.x < 0);
		sign[1] = (directionInv.y < 0);
		sign[2] = (directionInv.z < 0);
		originD = PrecisionVector3<double>(origin.x, origin.y, origin.z);
		directionD = PrecisionVector3<double>(direction.x, direction.y, direction.z);
	}

	Ogre::Vector3 origin;
	PrecisionVector3<double> originD;
	Ogre::Vector3 direction, directionInv;
	PrecisionVector3<double> directionD;
	int sign[3];

	struct Intersection
	{
		Intersection() : flags(0) {}
		double t;
		unsigned int flags;
		Ogre::Vector3 userData[1];
	};


	// Test the ray against intersection with plane given by a normal and a position.
	__forceinline bool intersectPlane(double &tOut, bool &backface, const PrecisionVector3<double> &planeNormal, const PrecisionVector3<double> &planePosition) const
	{
		double rayNormalAngle = planeNormal.dotProduct(directionD);
		if (std::abs(rayNormalAngle) == 0) return false;
		backface = rayNormalAngle > 0;
		//dot(t*rayDir, n) = -dot((origin - planePosition), planeNormal)
		tOut = -(originD - planePosition).dotProduct(planeNormal) / rayNormalAngle;
		return (tOut > 0);
	}

	// Test the ray against intersection with plane given by a normal and a position.
	__forceinline bool intersectPlaneFront(float &tOut, const Ogre::Vector3 &planeNormal, const Ogre::Vector3 &planePosition) const
	{
		float rayNormalAngle = planeNormal.dotProduct(direction);
		if (rayNormalAngle >= -std::numeric_limits<float>::epsilon()) return false;	// plane is parallel or faces away from the ray

		//dot(t*rayDir, n) = -dot(movedRayOrigin, planeNormal)
		tOut = -(origin - planePosition).dotProduct(planeNormal) / rayNormalAngle;
		return (tOut > 0.0f);
	}

	// Tests if the ray hits the plane at a closer distance than closestT
	__forceinline bool intersectPlaneFrontUpdate(float &tOut, float closestT, const Ogre::Vector3 &planeNormal, const Ogre::Vector3 &planePosition) const
	{
		float rayNormalAngle = planeNormal.dotProduct(direction);
		if (rayNormalAngle >= 0) return false;	// plane is parallel or faces away from the ray

		//dot(t*rayDir, n) = -dot(movedRayOrigin, planeNormal)
		tOut = -(origin - planePosition).dotProduct(planeNormal) / rayNormalAngle;
		return (tOut > 0.0f && tOut < closestT);
	}

	// Tests the ray against intersection with an AABB given by min and max vectors.
	// Source: http://www.cs.utah.edu/~awilliam/box/box.pdf
	virtual bool intersectAABB(const Ogre::Vector3 bounds[2], float tNear, float tFar) const
	{
		float tMin, tMax, t2Min, t2Max;

		tMin = (bounds[this->sign[0]].x - this->origin.x) * this->directionInv.x;
		tMax = (bounds[1-this->sign[0]].x - this->origin.x) * this->directionInv.x;

		t2Min = (bounds[this->sign[1]].y - this->origin.y) * this->directionInv.y;
		t2Max = (bounds[1-this->sign[1]].y - this->origin.y) * this->directionInv.y;

		if (tMin > t2Max || t2Min > tMax) return false;
		tMin = std::max(tMin, t2Min);
		tMax = std::min(tMax, t2Max);

		t2Min = (bounds[this->sign[2]].z - this->origin.z) * this->directionInv.z;
		t2Max = (bounds[1-this->sign[2]].z - this->origin.z) * this->directionInv.z;

		if (tMin > t2Max || t2Min > tMax) return false;
		tMin = std::max(tMin, t2Min);
		tMax = std::min(tMax, t2Max);

		return (tMin < tFar && tMax > tNear);
	}
	virtual bool intersectAABB(const Ogre::Vector3 bounds[2]) const
	{
		float tMin, tMax, t2Min, t2Max;

		tMin = (bounds[this->sign[0]].x - this->origin.x) * this->directionInv.x;
		tMax = (bounds[1-this->sign[0]].x - this->origin.x) * this->directionInv.x;

		t2Min = (bounds[this->sign[1]].y - this->origin.y) * this->directionInv.y;
		t2Max = (bounds[1-this->sign[1]].y - this->origin.y) * this->directionInv.y;

		if (tMin > t2Max || t2Min > tMax) return false;
		tMin = std::max(tMin, t2Min);
		tMax = std::min(tMax, t2Max);

		t2Min = (bounds[this->sign[2]].z - this->origin.z) * this->directionInv.z;
		t2Max = (bounds[1-this->sign[2]].z - this->origin.z) * this->directionInv.z;

		if (tMin > t2Max || t2Min > tMax) return false;
		tMin = std::max(tMin, t2Min);
		tMax = std::min(tMax, t2Max);

		return (tMax > 0.0f);
	}

	/// Tests the ray against intersection with a triangle (frontface and backface).
	__forceinline bool intersectTriangleBothSides(Intersection &intersection, const PrecisionVector3<double> &p1, const RayTriangleIntersectionData &triData) const
	{
		bool backface = false;

		if (!intersectPlane(intersection.t, backface, triData.normal, p1)) return false;
		if (backface) intersection.flags = 1;

		PrecisionVector3<double> intersectPosition = this->originD + intersection.t*this->directionD;
		double b1 = intersectPosition[triData.equationIndex1]-p1[triData.equationIndex1];
		double b2 = intersectPosition[triData.equationIndex2]-p1[triData.equationIndex2];

		//write barycentric coordinates to intersection.userData[0]
		double bx = triData.aInv*b1 + triData.bInv*b2;
		double by = triData.cInv*b1 + triData.dInv*b2;
		double bz = 1.0-bx-by;
		return ((bx >= 0) && (by >= 0) && (bz >= 0));
	}
};